;10M硬盘FAT16
;%define	_BOOT_DEBUG_	; 用于生成.COM文件易于调试

%ifdef	_BOOT_DEBUG_
	org  100h			; 调试状态，做成 .COM 文件, 可调试
%else
	org  7c00h			; BIOS将把引导扇区加载到0:7C00处，并开始执行
%endif

;==============================================================
%ifdef	_BOOT_DEBUG_
BaseOfStack		equ	100h	; 堆栈基地址(栈底, 从这个位置向低地址生长)
%else
BaseOfStack		equ	7c00h	; 堆栈基地址(栈底, 从这个位置向低地址生长)
%endif

BaseOfLoader		equ	9000h	; LOADER.BIN 被加载到的位置 ----  段地址
OffsetOfLoader	equ	100h	; LOADER.BIN 被加载到的位置 ---- 偏移地址
RootDirSectors	equ	32		; 根目录占用的扇区数(512*32/512 = 32)
SectorNoOfRootDirectory	equ	143	; 根目录区的首扇区号(1 + 125 + 1 + 8*2 = 143)
SectorNoOfFAT1	equ	127		; FAT#1的首扇区号 = 1 + 125 + BPB_RsvdSecCnt = 127
DeltaSectorNo		equ	127	; DeltaSectorNo =8*2 + 1 + 125 + 1 +  - 2*8 = 127(127+8*2+32=2*8+32+DeltaSectorNo)
							; 文件的开始扇区号 = 目录条目中的开始扇区号<<3 
							; + 根目录占用扇区数目 + DeltaSectorNo
;==============================================================

	jmp short LABEL_START		; 引导开始，跳转指令
	nop							; 这个 nop 不可少，无操作，占字节位

	; 下面是 FAT12 磁盘的头（BPB+EBPB，占51B）
	; BPB_BytsPerSec	DW 512		; 每扇区字节数
	; BPB_SecPerClus	DB 8		; 每簇多少扇区
	; BPB_RsvdSecCnt	DW 1		; Boot记录占用多少扇区
	; BPB_NumFATs	DB 2		; 共有多少 FAT 表
	; BPB_RootEntCnt	DW 512		; 根目录文件数最大值
	; BPB_TotSec16	DW 20160		; 逻辑扇区总数
	; BPB_Media		DB 0xF8		; 介质描述符
	; BPB_FATSz16	DW 8		; 每FAT扇区数
	; BPB_SecPerTrk	DW 63		; 每磁道扇区数
	; BPB_NumHeads	DW 2		; 磁头数(盘面数)
	; BPB_HiddSec		DD 0		; 隐藏扇区数
	; BPB_TotSec32	DD 0		; BPB_TotSec16为0时这个值记录扇区数
	; BS_DrvNum		DB 0x80		; 中断13h的驱动器号（硬/U盘时为0x80）
	; BS_Reserved1		DB 0		; 未使用
	; BS_BootSig		DB 29h		; 扩展引导标记 (29h)
	; BS_VolID		DD 25800016h; 卷序列号
	; BS_VolLab		DB 'MyOS System'; 卷标, 必须 11 个字节（不足补空格）
	; BS_FileSysType	DB 'FAT16   '	; 文件系统类型, 必须 8个字节（不足补空格）

LABEL_START:
	mov	ax, cs	; 置其他段寄存器值与CS相同
	mov	ds, ax	; 数据段
	mov	es, ax	; 附加段
	mov	ss, ax	; 堆栈段
	mov	sp, BaseOfStack ; 堆栈基址

; 清屏
	mov	ax, 600h		; AH = 6（功能号）、AL = 0（滚动文本行数，0为整个窗口）
	mov	bh, 7		; 黑底白字
	mov	cx, 0			; 左上角：(0, 0)
	mov	dx, 184fh		; 右下角：(24, 79)
	int	10h			; 显示中断

	mov	dh, 0		; "Booting  "
	call	DispStr		; 显示字符串

; 软驱复位
	xor	ah, ah	; 功能号ah=0（复位磁盘驱动器）
	mov	dl, 80h	; dl=80h（软驱，硬盘和U盘为80h）
	int	13h		; 磁盘中断

; 下面在A盘根目录中寻找 LOADER.BIN
	mov	word [wSectorNo], SectorNoOfRootDirectory 	; 给表示当前扇区号的
						; 变量wSectorNo赋初值为根目录区的首扇区号（=269）
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
	cmp	word [wRootDirSizeForLoop], 0	; 判断根目录区是否已读完
	jz	LABEL_NO_LOADERBIN		; 若读完则表示未找到LOADER.BIN
	dec	word [wRootDirSizeForLoop]	; 递减变量wRootDirSizeForLoop的值
	; 调用读扇区函数读入一个根目录扇区到装载区
	mov	ax, BaseOfLoader
	mov	es, ax			; ES <- BaseOfLoader（9000h）
	mov	bx, OffsetOfLoader	; BX <- OffsetOfLoader（100h）
	mov	ax, [wSectorNo]	; AX <- 根目录中的当前扇区号
	mov	cl, 1				; 只读一个扇区
	call	ReadSector		; 调用读扇区函数

	mov	si, LoaderFileName	; DS:SI -> "LOADER  BIN"
	mov	di, OffsetOfLoader	; ES:DI -> BaseOfLoader:0100
	cld					; 清除DF标志位
						; 置比较字符串时的方向为左/上[索引增加]
	mov	dx, 10h			; 循环次数=16（每个扇区有16个文件条目：512/32=16）
LABEL_SEARCH_FOR_LOADERBIN:
	cmp	dx, 0			; 循环次数控制
	jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; 若已读完一扇区
	dec	dx				; 递减循环次数值			  就跳到下一扇区
	mov	cx, 11			; 初始循环次数为11
LABEL_CMP_FILENAME:
	repe cmpsb			; 重复比较字符串中的字符，CX--，直到不相等或CX=0
	jcxz	LABEL_FILENAME_FOUND	; 如果比较了11个字符都相等，表示找到
LABEL_DIFFERENT:
	and	di, 0FFE0h		; DI &= E0为了让它指向本条目开头（低5位清零）
					; FFE0h = 1111111111100000（低5位=32=目录条目大小）
	add	di, 20h			; DI += 20h 下一个目录条目
	mov	si, LoaderFileName	; SI指向装载文件名串的起始地址
	jmp	LABEL_SEARCH_FOR_LOADERBIN; 转到循环开始处

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
	add	word [wSectorNo], 1 ; 递增当前扇区号
	jmp	LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_LOADERBIN:
	mov	dh, 2		; "No LOADER"
	call	DispStr		; 显示字符串
%ifdef	_BOOT_DEBUG_ ; 没有找到LOADER.BIN就回到 DOS
	mov	ax, 4c00h		; AH=4Ch（功能号，终止进程）、AL=0（返回代码）
	int	21h			; DOS软中断
%else
	jmp	$			; 没有找到 LOADER.BIN，在这里进入死循环
%endif

LABEL_FILENAME_FOUND:	; 找到 LOADER.BIN 后便来到这里继续
	; 计算文件的起始扇区号
	mov	ax, RootDirSectors	; AX=根目录占用的扇区数=32
	and	di, 0FFE0h		; DI -> 当前条目的开始地址 FFE0h = 1111111111100000（低5位=32=目录条目大小）
	add	di, 1Ah			; DI -> 文件的首扇区号在条目中的偏移地址 1Ah = 11010
	mov cx, word [es:di]	; CX=文件的首扇区号
	push cx				; 保存此扇区在FAT中的序号
	shl cx, 3 ; 文件相对起始扇区号：簇号*BPB_SecPerClus(8) add
	add	cx, ax			; CX=文件的相对起始扇区号+根目录占用的扇区数
	add	cx, DeltaSectorNo	; CL <- LOADER.BIN的起始扇区号(0-based)
	mov	ax, BaseOfLoader
	mov	es, ax			; ES <- BaseOfLoader（装载程序基址=9000h）
	mov	bx, OffsetOfLoader	; BX <- OffsetOfLoader（装载程序偏移地址=100h）
	mov	ax, cx			; AX <- 起始扇区号

LABEL_GOON_LOADING_FILE:
	push bx				; 保存装载程序偏移地址
	mov	cl, [BPB_SecPerClus]				; 1个簇 ; 1簇8个扇区 ;add
	call	ReadSector		; 读扇区

; 每读一个扇区就在 "Booting  " 后面打一个点, 形成这样的效果：Booting ......
	mov	ah, 0Eh		; 功能号（以电传方式显示单个字符）
	mov	al, '.'			; 要显示的字符
	mov	bl, 0Fh		; 黑底白字
	int	10h			; 显示中断

	; 计算文件的下一扇区号
	pop bx				; 取出装载程序偏移地址
	pop	ax				; 取出此扇区在FAT中的序号
	call	GetFATEntry		; 获取FAT项中的下一簇号
	cmp	ax, 0FF8h		; 是否是文件最后簇
	jae	LABEL_FILE_LOADED	; ≥FF8h时跳转，否则读下一个簇
	push ax				; 保存扇区在FAT中的序号
	mov	dx, RootDirSectors	; DX = 根目录扇区数 = 32
	shl ax, 3 ; 簇的相对起始扇区号 = 簇号*BPB_SecPerClus(8)
	add	ax, dx			; 扇区序号 + 根目录扇区数
	add	ax, DeltaSectorNo		; AX = 要读的数据扇区地址
	push ax
	mov ax, [BPB_BytsPerSec] ; 每扇区512Byte add
	shl ax, 3 ; 一簇8扇区共8*512 add
	add	bx, ax	; BX+512*8指向装载程序区的下一个扇区地址 modify
	pop ax
	jmp	LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:
	mov	dh, 1			; "Ready."
	call	DispStr			; 显示字符串

; **********************************************************************
	jmp	BaseOfLoader:OffsetOfLoader	; 这一句正式跳转到已加载到内
						; 存中的 LOADER.BIN 的开始处，
						; 开始执行 LOADER.BIN 的代码。
						; Boot Sector 的使命到此结束
; **********************************************************************

;==============================================================
;变量
	BPB_BytsPerSec	DW 512		; 每扇区字节数
	BPB_SecPerClus	DB 8		; 每簇扇区数
	BPB_SecPerTrk   DW 63       ; 每磁道扇区数
	BS_DrvNum       db 80h      ; 中断13h的驱动器号（硬/U盘时为0x80）

wRootDirSizeForLoop	dw	RootDirSectors	; 根目录区剩余扇区数
										; 初始化为14，在循环中会递减至零
wSectorNo		dw	0	; 当前扇区号，初始化为0，在循环中会递增
bOdd			db	0	; 奇数还是偶数FAT项

;字符串
LoaderFileName	db	"LOADER  BIN", 0 ; LOADER.BIN之文件名
; 为简化代码，下面每个字符串的长度均为MessageLength（=9），似串数组
MessageLength	equ	9
BootMessage:		db	"Booting  " 	;Booting  ; 9字节，不够则用空格补齐。序号0
Message1			db	"Ready.   " 	;Ready.   ; 9字节，不够则用空格补齐。序号1
Message2			db	"No LOADER" ;No LOADER; 9字节，不够则用空格补齐。序号2
;==============================================================

;----------------------------------------------------------------------------
; 函数名：DispStr
;----------------------------------------------------------------------------
; 作用：显示一个字符串，函数开始时DH中须为串序号(0-based)
DispStr:
	mov	ax, MessageLength ; 串长->AX（即AL=9）
	mul	dh				; AL*DH（串序号）->AX（=当前串的相对地址）
	add	ax, BootMessage	; AX+串数组的起始地址
	mov	bp, ax			; BP=当前串的偏移地址
	mov	ax, ds			; ES:BP = 串地址
	mov	es, ax			; 置ES=DS
	mov	cx, MessageLength	; CX = 串长（=9）
	mov	ax, 1301h			; AH = 13h（功能号）、AL = 01h（光标置于串尾）
	mov	bx, 0007h		; 页号为0(BH = 0) 黑底白字(BL = 07h)
	mov	dl, 0				; 列号=0
	int	10h				; 显示中断
	ret					; 函数返回
;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
; 函数名：ReadSector
;----------------------------------------------------------------------------
; 作用：从第 AX个扇区开始，将CL个扇区读入ES:BX中
ReadSector:
	; -----------------------------------------------------------------------
	; 怎样由扇区号求扇区在磁盘中的位置 (扇区号->柱面号、起始扇区、磁头号)
	; -----------------------------------------------------------------------
	; 设扇区号为 x
	;                           ┌ 柱面号 = y >> 1(BPB_NumHeads) 
	;       x           ┌ 商 y ┤
	;   -------------- 	=> ┤      └ 磁头号 = y & 1 取低1位
	;  每磁道扇区数     │
	;                   └ 余 z => 起始扇区号 = z + 1
	push bp		; 保存BP
	mov bp, sp	; 让BP=SP
	sub	sp, 2 	; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]
	mov	byte [bp-2], cl	; 压CL入栈（保存表示读入扇区数的传递参数）
	push bx			; 保存BX
	mov	bl, [BPB_SecPerTrk]	; BL=63（磁道扇区数）为除数
	div	bl			; AX/BL，商y在AL中、余数z在AH中
	inc	ah			; z ++（因磁盘的起始扇区号为1）
	mov	cl, ah		; CL <- 起始扇区号
	mov	dh, al		; DH <- y
	shr	al, 1			; y >> 1 （等价于y/BPB_NumHeads，软盘有2个磁头）
	mov	ch, al		; CH <- 柱面号
	and	dh, 1h		; DH & 1 = 磁头号
	pop	bx			; 恢复BX
	; 至此，"柱面号、起始扇区、磁头号"已全部得到
	mov	dl, [BS_DrvNum]	; 驱动器号（0表示软盘A, 0x80为硬盘/U盘）
.GoOnReading: ; 使用磁盘中断读入扇区
	mov	ah, 2				; 功能号（读扇区）
	mov	al, byte [bp-2]		; 读AL个扇区
	int	13h				; 磁盘中断
	jc	.GoOnReading	; 如果读取错误，CF会被置为1，
						; 这时就不停地读，直到正确为止
	add	sp, 2				; 栈指针+2
	pop	bp				; 恢复BP

	ret
;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
; 函数名：GetFATEntry
;----------------------------------------------------------------------------
; 作用：找到序号为AX的扇区在FAT中的条目，结果放在AX中。需要注意的
;     是，中间需要读FAT的扇区到ES:BX处，所以函数一开始保存了ES和BX
GetFATEntry:
	push es			; 保存ES、BX和AX（入栈）
	push bx
	push ax
; 设置读入的FAT扇区写入的基地址
	mov ax, BaseOfLoader	; AX=9000h
	sub	ax, 100h		; 在BaseOfLoader后面留出4K空间用于存放FAT
	mov	es, ax		; ES=8F00h
; 判断FAT项的奇偶
	pop	ax			; 取出FAT项序号（出栈）
	mov	byte [bOdd], 0; 初始化奇偶变量值为0（偶）
	mov	bx, 3		; AX*1.5 = (AX*3)/2
	mul	bx			; DX:AX = AX * 3（AX*BX 的结果值放入DX:AX中）
	mov	bx, 2		; BX = 2（除数）
	xor	dx, dx		; DX=0	
	div	bx			; DX:AX / 2 => AX <- 商、DX <- 余数
	cmp	dx, 0		; 余数 = 0（偶数）？
	jz LABEL_EVEN	; 偶数跳转
	mov	byte [bOdd], 1	; 奇数
LABEL_EVEN:		; 偶数
	; 现在AX中是FAT项在FAT中的偏移量，下面来
	; 计算FAT项在哪个扇区中(FAT占用不止一个扇区)
	xor	dx, dx		; DX=0	
	mov	bx, [BPB_BytsPerSec]	; BX=512
	div	bx			; DX:AX / 512
		  			; AX <- 商 (FAT项所在的扇区相对于FAT的扇区号)
		  			; DX <- 余数 (FAT项在扇区内的偏移)
	push dx			; 保存余数（入栈）
	mov bx, 0 		; BX <- 0 于是，ES:BX = 8F00h:0
	add	ax, SectorNoOfFAT1 ; 此句之后的AX就是FAT项所在的扇区号
	mov	cl, 2			; 读取FAT项所在的扇区，一次读两个，避免在边界
	call	ReadSector	; 发生错误, 因为一个 FAT项可能跨越两个扇区
	pop	dx			; DX= FAT项在扇区内的偏移（出栈）
	add	bx, dx		; BX= FAT项在扇区内的偏移
	mov	ax, [es:bx]	; AX= FAT项值
	cmp	byte [bOdd], 1	; 是否为奇数项？
	jnz	LABEL_EVEN_2	; 偶数跳转
	shr	ax, 4			; 奇数：右移4位（取高12位）
LABEL_EVEN_2:		; 偶数
	and	ax, 0FFFh	; 取低12位
LABEL_GET_FAT_ENRY_OK:
	pop	bx			; 恢复ES、BX（出栈）
	pop	es
	ret
;----------------------------------------------------------------------------

times 446-($ - $$) db 0 ; 填充剩余主引导记录(446B)余下部分 with 0
; 磁盘分区表
	DPT_Enable DB 0x80       ; 分区是否激活（0x80或0）
	DPT_StartHeadNumber DB 0 ; 开始磁头号
	DPT_StartSecCylinNumber DW 0x0101; 开始扇区号（低字节的低6位）和柱面号（其余10位，其中低位字节的高2位为其第9、10位）
	DPT_PartitionType DB 4 ; 分区类型（如：0x01 = FAT12、0x04/0x06/0x0E = FAT16、0x0B/0x0C = FAT32、0x07 = NTFS/ exFAT、0x05/0x0F = 扩展、0x83 = Linux、0xEE = GPT）
	DPT_endHeadNumber DB 1 ; 结束磁头号
	DPT_EndSecCylinNumber DW 0x9F3F ; 结束扇区号（低字节的低6位）和柱面号（其余10位，其中低位字节的高2位为其第9、10位）
	DPT_LBAvalue DD 126 ; 分区起始地址的LBA（逻辑块地址）值（扇区序号）
	DPT_PartitionSize DD 20034 ; 分区大小 (总扇区数)

	times 510-($-$$) db 0	; 用0填充剩下的扇区空间
	db 	55h, 0aah			; 引导扇区结束标志

; 填充主引导扇区后面的251个扇区的空白
	times 512*125 db 0

; 第二个柱面
; 分区引导扇区(占1个扇区)
_SEG:
	org  07c00h					; 加载到 0:7C00 处
	; 下面是跳转指令（占3B）：
	jmp short LABEL_START2		; 跳转到代码起始处
	nop							; 这个nop（无操作指令）不可少（占1个字节位）
	; 下面是OEM名串（占8B）：
	_BS_OEMName	DB 'MyOS CXF' ; OEM String, 必须 8 个字节（不足补空格）
	; 下面是 FAT12 磁盘的头（BPB+EBPB，占51B）
	_BPB_BytsPerSec	DW 512		; 每扇区字节数
	_BPB_SecPerClus	DB 8		; 每簇多少扇区
	_BPB_RsvdSecCnt	DW 1		; Boot记录占用多少扇区
	_BPB_NumFATs	DB 2		; 共有多少 FAT 表
	_BPB_RootEntCnt	DW 512		; 根目录文件数最大值
	_BPB_TotSec16	DW 20160		; 逻辑扇区总数
	_BPB_Media		DB 0xF8		; 介质描述符
	_BPB_FATSz16	DW 8		; 每FAT扇区数
	_BPB_SecPerTrk	DW 63		; 每磁道扇区数
	_BPB_NumHeads	DW 2		; 磁头数(盘面数)
	_BPB_HiddSec		DD 0		; 隐藏扇区数
	_BPB_TotSec32	DD 0		; BPB_TotSec16为0时这个值记录扇区数
	_BS_DrvNum		DB 0x80		; 中断13h的驱动器号（硬/U盘时为0x80）
	_BS_Reserved1		DB 0		; 未使用
	_BS_BootSig		DB 29h		; 扩展引导标记 (29h)
	_BS_VolID		DD 34506789h; 卷序列号
	_BS_VolLab		DB 'Hello Hello'; 卷标, 必须 11 个字节（不足补空格）
	_BS_FileSysType	DB 'FAT16   '	; 文件系统类型, 必须 8个字节（不足补空格）  
LABEL_START2:
	mov	ax, cs		; 置DS和ES=CS
	mov	ds, ax
	mov	es, ax
	call	DispStr2		; 调用显示字符串例程
	jmp	$			; 无限循环
DispStr2:
	mov ah,13h 		; BIOS中断的功能号（显示字符串）
	mov al,1 			; 光标放到串尾
	mov bh,0 		; 页号=0
	mov bl,0ch 		; 字符颜色=黑底亮红字
	mov cx,18 		; 串长=16
	mov dx,0 		; 显示串的起始位置（0，0）：DH=行号、DL=列号
	mov bp,BootMsg2	; ES:BP=串地址
	int 10h 			; 调用10H号显示中断
	ret				; 从例程返回
BootMsg2:  db  "Hello, MyOS world!" ; 显示用字符串
times 510-($-_SEG) db 0	; 用0填充剩下的扇区空间
db 	55h, 0aah			; 分区引导扇区结束标志

; 填充两个FAT表的头两个项（每个FAT占8个扇区）
	db 0f8h, 0ffh, 0ffh			; 介质描述符（F8h）和Fh、结束簇标志项FFFh
	db 03h, 40h, 0, 05h, 60h, 0, 07h, 0f0h, 0ffh ; loader.bin 条目
	times 512*8-3-9		db	0	; 用0填充FAT#1剩下的空间
	db 0f8h, 0ffh, 0ffh			; 介质描述符（F8h）和Fh、结束簇标志项FFFh
	db 03h, 40h, 0, 05h, 60h, 0, 07h, 0f0h, 0ffh ; loader.bin 条目
	times 512*8-3-9		db	0	; 用0填充FAT#2剩下的空间
; 根目录中的卷标条目
	db 'HELLO HELLO' 			; 卷标, 必须 11 个字节（不足补空格）
	db 8						; 文件属性值（卷标条目的为08h）
	dw 0,0,0,0,0				; 10个保留字节
	dw 75A0h,4A7Fh			; 创建时间，设为2017年3月31日14时45分0秒
	dw 0						; 开始簇号（卷标条目的必需为0）
	dd 0						; 文件大小（也设为0）

; loader.bin 条目
	db 'LOADER  BIN'
	db 0h
	dw 18h,0,0,0,0
	dw 75A0h,4A7Fh
	dw 2
	dd 5008h
times 512*32 - 32*2 db 0
; data zone
LABEL_SHOW_L:
;org	100h		; 可用于生成调试用的COM文件
	mov	ax, 0B800h	; 文本窗口所对应的显存起始地址
	mov	gs, ax		; GS是80386 CPU新增加的一个附加段寄存器
	mov	ah, 0Fh		; AH = 字符属性质，0(不闪)000：黑底、1111：亮白字
	mov	al, 'L'		; AL = 字符的ASCII码
	mov	[gs:((80 * 0 + 39) * 2)], ax	; 屏幕第 0 行, 第 39 列
	jmp	LABEL_SHOW_O
	times 512*8-($-LABEL_SHOW_L)	db	0		; 用0填充扇区剩余空间
LABEL_SHOW_O:
	mov	al, 'O'
	mov	[gs:((80 * 1 + 39) * 2)], ax	; 屏幕第 1 行, 第 39 列
	jmp	LABEL_SHOW_A
	times 2*8*512-($-LABEL_SHOW_L)	db	0		; 用0填充扇区剩余空间
LABEL_SHOW_A:
	mov	al, 'A'
	mov	[gs:((80 * 2 + 39) * 2)], ax	; 屏幕第 2 行, 第 39 列
	jmp	LABEL_SHOW_D
	times 3*8*512-($-LABEL_SHOW_L)	db	0		; 用0填充扇区剩余空间
LABEL_SHOW_D:
	mov	al, 'D'
	mov	[gs:((80 * 3 + 39) * 2)], ax	; 屏幕第 3 行, 第 39 列
	jmp LABEL_SHOW_O2
	times 4*8*512-($ - LABEL_SHOW_L) db 0       ; 用0填充扇区剩余空间
LABEL_SHOW_O2:
	mov	al, 'O'
	mov	[gs:((80 * 4 + 39) * 2)], ax	; 屏幕第 4 行, 第 39 列
	jmp	LABEL_SHOW_S
	times 5*8*512-($ - LABEL_SHOW_L)	db	0		; 用0填充扇区剩余空间
LABEL_SHOW_S:
	mov	al, 'S'
	mov	[gs:((80 * 5 + 39) * 2)], ax	; 屏幕第 5 行, 第 39 列
	jmp	$						; 到此停住，进入死循环
